#include "stdafx.hpp"
#include "ColligereCddbDiscId.hpp"
#include "ColligereUtils.hpp"

#ifdef __LINUX__
#include "./Ext/DiscId-0.1.3/src/linux/IoCtlDisc.h"
#endif
#ifdef __APPLE__
#include "./Ext/DiscId-0.1.3/src/mac/MacIoCtlDisc.h"
#endif
#ifdef __WINDOWS__
#include "./Ext/DiscId-0.1.3/src/win32/MciDisc.h"
#endif

col::string_deque col::CddbDiscId::msMountedVolumes;

col::CddbDiscId::CddbDiscId() : mTrackCount(0), mDiscLengthInSecs(0), mCddbDiscId(0) 
{ 
}

col::CddbDiscId::CddbDiscId ( const wxString &rDevice ) 
: mTrackCount(0), mDevice ( rDevice ), mDiscLengthInSecs(0), mCddbDiscId(0) 
{ 
}

void col::CddbDiscId::SetDevice ( const wxString &rDevice )
{
	mDevice = rDevice;

	mTrackCount = 0;
	mCddbDiscId = 0;
	mDiscLengthInSecs = 0;

	mTrackStartFrames.clear();
}

wxUint32 col::CddbDiscId::GetId() const
{
	return mCddbDiscId;
}

unsigned long col::CddbDiscId::GetDiscLengthInSeconds() const 
{
	return mDiscLengthInSecs;
}

wxUint32 col::CddbDiscId::GetTrackCount() const
{
	return mTrackCount; 
}

wxInt32 col::CddbDiscId::CalcSum ( wxInt32 n )
{
	wxInt32 result = 0;
	while ( n > 0 )
	{
		result += n % 10;
		n /= 10;
	}
	return result;
}

const wxString &col::CddbDiscId::GetTrackLength ( wxUint32 track ) const
{
	std::map < wxUint32, wxString >::const_iterator it = mTrackLengthMap.find ( track );
	return it != mTrackLengthMap.end() ? it->second : empty_string;
}

bool col::CddbDiscId::CalculateId()
{
	mTrackCount = 0;
	mCddbDiscId = 0;
	mDiscLengthInSecs = 0;

	mTrackStartFrames.clear();

	try
	{
		boost::scoped_ptr < COSDisc > disc ( new COSDisc ( mDevice.c_str() ) );
	
		mTrackCount = disc->GetTrackCount();

		if ( mTrackCount > 0 )
		{
			wxUint32 n = 0;

			for ( wxUint32 track = 0; track < mTrackCount; ++track )
			{
				n += CalcSum ( disc->GetStartFrame ( track ) / 
					CDiscHelper::GetFramesPerSecond() );
			}
			const wxUint32 start_sec = disc->GetStartFrame(0) / 
				CDiscHelper::GetFramesPerSecond();
			
			const wxUint32 leadout_sec = ( disc->GetStartFrame 
				( mTrackCount - 1 ) + disc->GetFrames ( mTrackCount - 1 ) ) / 
				CDiscHelper::GetFramesPerSecond();
			
			const wxUint32 total = leadout_sec - start_sec;

			mCddbDiscId = ( ( n % 0xff ) << 24 | total << 8 | mTrackCount );

			for ( wxUint32 index = 0; index < mTrackCount; ++index )
			{
				mTrackStartFrames.push_back ( disc->GetStartFrame ( index ) );
			}
			const unsigned long leadout_frame = disc->GetStartFrame 
				( mTrackCount - 1 ) + disc->GetFrames ( mTrackCount - 1 );
				
			mDiscLengthInSecs = leadout_frame / CDiscHelper::GetFramesPerSecond();

#ifdef __WXMSW__
			for ( wxUint32 t = 0; t < disc->GetTracks().size(); ++t )
			{
				mTrackLengthMap[t+1] = disc->GetTracks()[t].GetLength();
			}
#endif
			return true;
		}
	}
	catch ( std::runtime_error &rErr )
	{
		wxLogError ( _("Disc ID Error: %s"), rErr.what() );
	}
	return false;
}

const col::TrackStartFrameDeque &col::CddbDiscId::GetTrackStartFrames() const
{
	return mTrackStartFrames;
}

const col::string_deque &col::CddbDiscId::GetMountedDrives ( bool forceReQuery )
{
#ifdef __WXMSW__
	if ( msMountedVolumes.empty() || forceReQuery )
	{
		msMountedVolumes.clear();

		const wxArrayString drives ( wxFSVolumeBase::GetVolumes 
			( wxFS_VOL_MOUNTED | wxFS_VOL_REMOVABLE ) );

		for ( wxUint32 i = 0; i < drives.GetCount(); ++i )
		{
			const wxFSVolumeBase cur ( drives[i] );

			if ( cur.IsOk() && ( cur.GetKind() == wxFS_VOL_CDROM || 
				cur.GetKind() == wxFS_VOL_DVDROM ) )
			{
				msMountedVolumes.push_back ( cur.GetName() );
			}
		}
	}
#endif
	return msMountedVolumes;
}
